import subprocess

from prettytable import PrettyTable

from tools import normalize_route_info, normalize_interface_ip_index, normalize_interface_index_name, \
    normalize_interface_ip_netmask, normalize_interface_info, \
    normalize_route_info_interface_index, format_cli_output
from interface import Interface


class Router:

    def __init__(self, device_type, device_name, manage_ip, auth_type, snmp_port, snmp_password, ssh_port, ssh_username,
                 ssh_password):
        """
        初始化Router类的实例。

        :param device_type: 设备类型
        :param device_name: 设备名称
        :param manage_ip: 管理IP地址
        :param auth_type: 认证类型（如SNMP，SSH等）
        :param snmp_port: SNMP端口号
        :param snmp_password: SNMP密码
        :param ssh_port: SSH端口号
        :param ssh_username: SSH用户名
        :param ssh_password: SSH密码
        """
        self.device_type = device_type  # 设备类型
        self.device_name = device_name  # 设备名称
        self.manage_ip = manage_ip  # 管理IP地址
        self.auth_type = auth_type  # 认证类型
        self.snmp_port = snmp_port  # SNMP端口号
        self.snmp_password = snmp_password  # SNMP密码
        self.ssh_port = ssh_port  # SSH端口号
        self.ssh_username = ssh_username  # SSH用户名
        self.ssh_password = ssh_password  # SSH密码
        self.interface_ip_index_info = self.get_interface_ip_index_info()
        self.interface_index_name_info = self.get_interface_index_name_info()
        self.interface_ip_netmask_info = self.get_interface_ip_netmask_info()
        self.interface_info = self.combine_interface_info()

        self.route_info_proto = self.get_ip_route_table_proto()
        self.route_info_interface = self.get_ip_route_table_interface()
        self.route_info = self.combine_route_table()

    # 合并路由信息，获得最终路由表
    def combine_route_table(self):
        route_info_proto = self.route_info_proto
        route_info_interface = self.route_info_interface
        route_info_combined = []
        for route_info_proto_element in route_info_proto:
            for route_info_interface_element in route_info_interface:
                if route_info_proto_element["destination"] == route_info_interface_element["destination"]:
                    route_info_proto_element["interface"] = route_info_interface_element["interface"]
                    route_info_combined.append(route_info_proto_element)
        print("设备{}路由表: ".format(self.device_name))

        format_cli_output(rows=[[[route_info_combined_element["destination"]],
                                 [route_info_combined_element["net_mask"]],
                                 [route_info_combined_element["nexthop"]],
                                 [route_info_combined_element["interface"]],
                                 [route_info_combined_element["proto_type"]]] for route_info_combined_element in
                                route_info_combined],
                          headers=["destination", "net_mask", "nexthop", "interface", "proto_type"])

        return route_info_combined

    # 通过snmp获得路由——proto信息
    def get_ip_route_table_proto(self):
        route_table = []
        # print("获取到设备 {} 的路由信息".format(self.device_name))
        snmp_command = './snmptool/snmpwalk.exe -v 2c -c {} {} {}' \
            .format(self.snmp_password, self.manage_ip, "1.3.6.1.2.1.4.24.4.1.6")
        completed_process = subprocess.Popen(snmp_command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = completed_process.communicate()
        if len(stdout) > 0:
            snmp_message = stdout.decode()
            route_table = normalize_route_info(snmp_message)
            # print("设备 {} 的路由信息为：\n{}".format(self.device_name, route_table))
        elif len(stderr) > 0:
            print("设备 {} 的路由信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
            raise Exception("设备 {} 的接口信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
        return route_table

    #  snmp获取路由__interface信息
    def get_ip_route_table_interface(self):
        route_table = []
        # print("获取到设备 {} 的路由信息".format(self.device_name))
        snmp_command = './snmptool/snmpwalk.exe -v 2c -c {} {} {}' \
            .format(self.snmp_password, self.manage_ip, "1.3.6.1.2.1.4.24.4.1.5")
        completed_process = subprocess.Popen(snmp_command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = completed_process.communicate()
        if len(stdout) > 0:
            snmp_message = stdout.decode()
            route_table = normalize_route_info_interface_index(snmp_message)
            # print("设备 {} 的路由信息为：\n{}".format(self.device_name, route_table))
        elif len(stderr) > 0:
            print("设备 {} 的路由信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
            raise Exception("设备 {} 的接口信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
        return route_table

    # snmp获取接口index信息，格式为ip = index
    #  示例： .1.3.6.1.2.1.4.20.1.2.10.0.10.2 = INTEGER: 4
    def get_interface_ip_index_info(self):
        # print("获取到设备 {} 的接口信息".format(self.device_name))
        interface_ip_index_list = []
        snmp_command = './snmptool/snmpwalk.exe -v 2c -c {} {} {}' \
            .format(self.snmp_password, self.manage_ip, "1.3.6.1.2.1.4.20.1.2")
        completed_process = subprocess.Popen(snmp_command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = completed_process.communicate()
        if len(stdout) > 0:
            snmp_message = stdout.decode()
            interface_ip_index_list = normalize_interface_ip_index(snmp_message)
            # print("设备 {} 的接口信息为： {}".format(self.device_name, interface_ip_index_list))
        elif len(stderr) > 0:
            print("设备 {} 的接口信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
            raise Exception("设备 {} 的接口信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
        return interface_ip_index_list

    # snmp获取接口index信息，格式为 index = name
    # 示例： .1.3.6.1.2.1.31.1.1.1.18.4 = STRING: "HUAWEI, AR Series, GigabitEthernet0/0/1 Interface"
    def get_interface_index_name_info(self):
        # print("获取到设备 {} 的接口信息".format(self.device_name))
        interface_index_name_list = []
        snmp_command = './snmptool/snmpwalk.exe -v 2c -c {} {} {}' \
            .format(self.snmp_password, self.manage_ip, "1.3.6.1.2.1.31.1.1.1.18")
        completed_process = subprocess.Popen(snmp_command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = completed_process.communicate()
        if len(stdout) > 0:
            snmp_message = stdout.decode()
            interface_index_name_list = normalize_interface_index_name(snmp_message)
            # print("设备 {} 的接口信息为： {}".format(self.device_name, interface_index_name_list))
        elif len(stderr) > 0:
            print("设备 {} 的接口信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
            raise Exception("设备 {} 的接口信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
        return interface_index_name_list

    # snmp获取接口index信息，格式为 ip = netmask
    # 示例： .1.3.6.1.2.1.4.20.1.3.10.0.10.2 = IpAddress: 255.255.255.252
    def get_interface_ip_netmask_info(self):
        # print("获取到设备 {} 的接口信息".format(self.device_name))
        interface_ip_netmask_list = []
        snmp_command = './snmptool/snmpwalk.exe -v 2c -c {} {} {}' \
            .format(self.snmp_password, self.manage_ip, "1.3.6.1.2.1.4.20.1.3")
        completed_process = subprocess.Popen(snmp_command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        stdout, stderr = completed_process.communicate()
        if len(stdout) > 0:
            snmp_message = stdout.decode()
            interface_ip_netmask_list = normalize_interface_ip_netmask(snmp_message)
            # print("设备 {} 的接口信息为： {}".format(self.device_name, interface_ip_netmask_list))
        elif len(stderr) > 0:
            print("设备 {} 的接口信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
            raise Exception("设备 {} 的接口信息获取错误，错误信息为： {}".format(self.device_name, stderr.decode()))
        return interface_ip_netmask_list

    # 合并所有接口信息
    def combine_interface_info(self):
        interface_ip_index_info = self.interface_ip_index_info
        interface_index_name_info = self.interface_index_name_info
        interface_ip_netmask_info = self.interface_ip_netmask_info
        interface_info_temp = []
        interface_info = []
        for interface_ip_index_info_element in interface_ip_index_info:
            for interface_index_name_info_element in interface_index_name_info:
                if interface_ip_index_info_element["index"] == interface_index_name_info_element["index"]:
                    interface_ip_index_info_element["name"] = interface_index_name_info_element["name"]
                    interface_info_temp.append(interface_ip_index_info_element)
        for interface_info_element in interface_info_temp:
            for interface_ip_netmask_info_element in interface_ip_netmask_info:
                if interface_info_element["ip"] == interface_ip_netmask_info_element["ip"]:
                    interface_info.append(Interface(interface_info_element["index"], interface_info_element["name"],
                                                    interface_info_element["ip"],
                                                    interface_ip_netmask_info_element["net_mask"]))
        print("设备{}接口（使用中）：".format(self.device_name))
        for interface in interface_info:
            interface.display_info()
        return interface_info

    def print_device_details(self):
        print("读取到路由器{}的详细信息如下：")
        print("读取到路由器{}接口（使用中）：".format(self.device_name))
        for interface in self.interface_info:
            interface.display_info()
        print("读取到路由器{}路由表: ".format(self.device_name))
        format_cli_output(rows=[[[route_info_combined_element["destination"]],
                                 [route_info_combined_element["net_mask"]],
                                 [route_info_combined_element["nexthop"]],
                                 [route_info_combined_element["interface"]],
                                 [route_info_combined_element["proto_type"]]] for route_info_combined_element in
                                self.route_info],
                          headers=["destination", "net_mask", "nexthop", "interface", "proto_type"])


if __name__ == '__main__':
    router_test = Router(device_type="Huawei_AR2220",
                         device_name="AR8",
                         manage_ip="10.0.10.2",
                         auth_type="snmp",
                         snmp_port=161,
                         snmp_password="public",
                         ssh_port=22,
                         ssh_username="admin",
                         ssh_password="123"
                         )
